package generate

import (
	"errors"
	"fmt"
	"os"
	"path/filepath"
	"strings"

	"github.com/zeromicro/go-zero/tools/goctl/config"
	"github.com/zeromicro/go-zero/tools/goctl/model/mongo/template"
	"github.com/zeromicro/go-zero/tools/goctl/util"
	"github.com/zeromicro/go-zero/tools/goctl/util/pathx"
	"github.com/zeromicro/go-zero/tools/goctl/util/stringx"
)

// Context defines the model generation data what they needs
type Context struct {
	Types   []string
	Cache   bool
	Easy    bool
	Output  string
	Cfg     *config.Config
	Package string
}

// Do executes model template and output the result into the specified file path
func Do(ctx *Context) error {
	if ctx.Cfg == nil {
		return errors.New("missing config")
	}

	fmt.Println(ctx.Package)

	if err := generateTypes(ctx); err != nil {
		fmt.Println("generateTypes")
		return err
	}

	if err := generateModel(ctx); err != nil {
		fmt.Println("generateModel")
		return err
	}

	if err := generateTool(ctx); err != nil {
		fmt.Println("generateTool")
		return err
	}

	return generateError(ctx)
}

func generateModel(ctx *Context) error {

	for _, t := range ctx.Types {
		fn := strings.ToLower(t) + "model"

		text, err := pathx.LoadTemplate(category, modelTemplateFile, template.ModelText)
		if err != nil {
			return err
		}

		output := filepath.Join(ctx.Output, fn+".go")
		exists, err := PathExists(output)
		if err != nil || exists {
			return err
		}

		if err = util.With("model").Parse(text).GoFmt(true).SaveTo(map[string]any{
			"Type":      stringx.From(t).Title(),
			"lowerType": stringx.From(t).Untitle(),
			"Cache":     ctx.Cache,
			"Package":   ctx.Package,
			"Table":     ConvertToSnakeCase(t),
		}, output, true); err != nil {
			return err
		}
	}

	return nil
}

func generateTypes(ctx *Context) error {
	for _, t := range ctx.Types {
		fn := strings.ToLower(t) + "types"
		text, err := pathx.LoadTemplate(category, modelTypesTemplateFile, template.ModelTypesText)
		if err != nil {
			return err
		}

		output := filepath.Join(ctx.Output, fn+".go")
		exists, err := PathExists(output)
		if err != nil || exists {
			return err
		}

		if err = util.With("model").Parse(text).GoFmt(true).SaveTo(map[string]any{
			"Type":    stringx.From(t).Title(),
			"Package": ctx.Package,
		}, output, false); err != nil {
			return err
		}
	}

	return nil
}

func generateError(ctx *Context) error {
	text, err := pathx.LoadTemplate(category, errTemplateFile, template.Error)
	if err != nil {
		return err
	}

	output := filepath.Join(ctx.Output, "error.go")
	exists, err := PathExists(output)
	if err != nil || exists {
		return err
	}

	return util.With("error").Parse(text).GoFmt(true).SaveTo(map[string]any{
		"Package": ctx.Package,
	}, output, false)
}

func generateTool(ctx *Context) error {
	text, err := pathx.LoadTemplate(category, toolFile, template.Tool)
	if err != nil {
		return err
	}

	output := filepath.Join(ctx.Output, "tool.go")
	exists, err := PathExists(output)
	if err != nil || exists {
		return err
	}

	return util.With("tool").Parse(text).GoFmt(true).SaveTo(map[string]any{
		"Package": ctx.Package,
	}, output, false)
}
func ConvertToSnakeCase(str string) string {
	var result string
	for i, char := range str {
		if i > 0 && 'A' <= char && char <= 'Z' {
			result += "_" + string(char)
		} else {
			result += string(char)
		}
	}
	return strings.ToLower(result)
}
func PathExists(path string) (bool, error) {
	_, err := os.Stat(path)
	if err == nil {
		return true, nil
	}
	if os.IsNotExist(err) {
		return false, nil
	}
	return false, err
}
